/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H
#define ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H

#include "plugins/ecmascript/runtime/base/builtins_base.h"
#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h"
#include "plugins/ecmascript/runtime/js_tagged_value.h"
#include "plugins/ecmascript/runtime/regexp/regexp_executor.h"
#include "runtime/regexp/ecmascript/regexp_parser.h"

namespace ark::ecmascript::builtins {
using MatchResult = RegExpMatchResult<JSHandle<EcmaString>>;
using RegExpExecutor = ark::ecmascript::RegExpExecutor;

class RegExpExecResultCache : public TaggedArray {
public:
    enum CacheType { REPLACE_TYPE, SPLIT_TYPE, MATCH_TYPE, EXEC_TYPE };
    static RegExpExecResultCache *Cast(TaggedObject *object)
    {
        return reinterpret_cast<RegExpExecResultCache *>(object);
    }
    static JSTaggedValue CreateCacheTable(JSThread *thread);
    // extend as an additional parameter to judge cached
    JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
                                   const JSHandle<JSTaggedValue> &flags, const JSHandle<JSTaggedValue> &input,
                                   CacheType type, const JSHandle<JSTaggedValue> &regexp,
                                   JSTaggedValue extend = JSTaggedValue::Undefined());
    // extend as an additional parameter to judge cached
    static void AddResultInCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache,
                                 const JSHandle<JSTaggedValue> &pattern, const JSHandle<JSTaggedValue> &flags,
                                 const JSHandle<JSTaggedValue> &input, const JSHandle<JSTaggedValue> &resultArray,
                                 CacheType type, uint32_t lastIndex, JSTaggedValue extend = JSTaggedValue::Undefined());

    static void GrowRegexpCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache);

    void ClearEntry(JSThread *thread, int entry);
    void SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input,
                  JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue);
    void UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type);
    bool Match(int entry, JSTaggedValue &patternStr, JSTaggedValue &flagsStr, JSTaggedValue &inputStr,
               JSTaggedValue &extend);
    inline void SetHitCount(JSThread *thread, int hitCount)
    {
        Set(thread, CACHE_HIT_COUNT_INDEX, JSTaggedValue(hitCount));
    }

    inline int GetHitCount()
    {
        return Get(CACHE_HIT_COUNT_INDEX).GetInt();
    }

    inline void SetCacheCount(JSThread *thread, int hitCount)
    {
        Set(thread, CACHE_COUNT_INDEX, JSTaggedValue(hitCount));
    }

    inline int GetCacheCount()
    {
        return Get(CACHE_COUNT_INDEX).GetInt();
    }

    void Print()
    {
        std::cout << "cache count: " << GetCacheCount() << std::endl;
        std::cout << "cache hit count: " << GetHitCount() << std::endl;
    }

    inline void SetLargeStrCount(JSThread *thread, uint32_t newCount)
    {
        Set(thread, LARGE_STRING_COUNT_INDEX, JSTaggedValue(newCount));
    }

    inline void SetConflictCount(JSThread *thread, uint32_t newCount)
    {
        Set(thread, CONFLICT_COUNT_INDEX, JSTaggedValue(newCount));
    }

    inline void SetStrLenThreshold(JSThread *thread, uint32_t newThreshold)
    {
        Set(thread, STRING_LENGTH_THRESHOLD_INDEX, JSTaggedValue(newThreshold));
    }

    inline uint32_t GetLargeStrCount()
    {
        return Get(LARGE_STRING_COUNT_INDEX).GetInt();
    }

    inline uint32_t GetConflictCount()
    {
        return Get(CONFLICT_COUNT_INDEX).GetInt();
    }

    inline uint32_t GetStrLenThreshold()
    {
        return Get(STRING_LENGTH_THRESHOLD_INDEX).GetInt();
    }

    inline void SetCacheLength(JSThread *thread, int length)
    {
        Set(thread, CACHE_LENGTH_INDEX, JSTaggedValue(length));
    }

    inline int GetCacheLength()
    {
        return Get(CACHE_LENGTH_INDEX).GetInt();
    }

private:
    static constexpr int DEFAULT_LARGE_STRING_COUNT = 10;
    static constexpr int DEFAULT_CONFLICT_COUNT = 100;
    static constexpr int INITIAL_CACHE_NUMBER = 0x10;
    static constexpr int DEFAULT_CACHE_NUMBER = 0x1000;
    static constexpr int CACHE_COUNT_INDEX = 0;
    static constexpr int CACHE_HIT_COUNT_INDEX = 1;
    static constexpr int LARGE_STRING_COUNT_INDEX = 2;
    static constexpr int CONFLICT_COUNT_INDEX = 3;
    static constexpr int STRING_LENGTH_THRESHOLD_INDEX = 4;
    static constexpr int CACHE_LENGTH_INDEX = 5;
    static constexpr int CACHE_TABLE_HEADER_SIZE = 6;
    static constexpr int PATTERN_INDEX = 0;
    static constexpr int FLAG_INDEX = 1;
    static constexpr int INPUT_STRING_INDEX = 2;
    static constexpr int LAST_INDEX_INDEX = 3;
    static constexpr int RESULT_REPLACE_INDEX = 4;
    static constexpr int RESULT_SPLIT_INDEX = 5;
    static constexpr int RESULT_MATCH_INDEX = 6;
    static constexpr int RESULT_EXEC_INDEX = 7;
    // Extend index used for saving an additional parameter to judge cached
    static constexpr int EXTEND_INDEX = 8;
    static constexpr int ENTRY_SIZE = 9;
};
}  // namespace ark::ecmascript::builtins
#endif  // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H
